# -*- coding: utf-8 -*-
'''
Created on 2013-4-11

@author: cl.lam
'''
import traceback
from datetime import datetime as dt
from flask import current_app as app
from flask import g, render_template, flash, session, redirect, url_for, request, abort
from flask.blueprints import Blueprint
from flask.helpers import jsonify
from sqlalchemy.sql.expression import and_, desc, or_

from sys2do.views import BasicView
from sys2do.util.decorator import templated, loginRequired, mypaginate, \
    allPermission, activetab
from sys2do.model import db, qry, DN, Shop, Product, DNDtl, SysLog, \
    InventoryNote, InventoryNoteDtl, InventoryProduct, InventoryLocation

from sys2do.util.common import _gld, _gp, _g, _gl
from sys2do.constant import *
from sys2do.util.logic_helper import getCurrentShopID, getCurrentShopProfile, \
    getCurrentUserID, makeUpdateLog, checkUpdate



__all__ = ['bpTns']

bpTns = Blueprint( 'bpTns', __name__ )

class TransferView( BasicView ):

    template_folder = "tns"

    @templated( "index.html" )
    @mypaginate( 'result' )
    @activetab( TAB_HOME )
    @allPermission( ['DN_VIEW'] )
    @loginRequired
    def index( self ):
        if request.method == 'POST':
            form = srhForm( request.form )
            session[url_for( '.view' )] = form
        else:
            form = session.get( url_for( '.view' ), srhForm( request.form ) )
        form.url = url_for( '.view' )

        spobj = getCurrentShopProfile()
        cds = [ DN.active == 0, DN.sShopID == spobj.shopID, ]
        if form.no.data : cds.append( DN.no.like( '%%%s%%' % form.no.data ) )
        if form.createTimeFrom.data : cds.append( DN.createTime > form.createTimeFrom.data )
        if form.createTimeTo.data : cds.append( DN.createTime < form.createTimeTo.data )

        result = DN.iall( and_( cds ), order_by = desc( DN.createTime ) )

        return {'form' : form , 'result' : result}


    @templated( "view.html" )
    @allPermission( ['DN_VIEW'] )
    @loginRequired
    def view( self ):
        obj = DN.get( _g( 'id' ) )
        return {'obj' : obj}


    @templated( "add.html" )
    @allPermission( ['DN_CREATE'] )
    @loginRequired
    def add( self ):
        spobj = getCurrentShopProfile()
        shops = qry( Shop ).filter( and_( Shop.active == 0, Shop.id != spobj.shopID ) ).order_by( Shop.name )
        return {'shops' : shops, }


    @allPermission( ['DN_CREATE'] )
    def saveNew( self ):
        try:
            params = _gld( 'awb', 'dShopID', 'remark', 'expectDate', 'fee', 'createTime' )
            params['sShopID'] = getCurrentShopID()
            hdr = DN.create( params )
            db.add( hdr )

            for ( k, v ) in _gp( 'dtl_qty_' ):
                n1, n2, pid, vid = k.split( "_" )
                tid = "%s_%s" % ( pid, vid )
                qty = _g( 'dtl_qty_%s' % tid )
                rmk = _g( 'dtl_rmk_%s' % tid )
                if not qty or int( qty ) < 1 : continue
                dtl = DNDtl.create( dict( hdr = hdr, pdtID = pid, qty = qty, remark = rmk, sIvtLtnID = vid ) )
                dtl.copyPdtInfo( Product.get( pid ) )
                db.add( dtl )

            db.flush()
            db.add( SysLog( refClz = hdr.__class__.__name__, type = LOG_TYPE_CREATE, refID = hdr.id, ) )
            db.commit()
            flash( MSG_SAVE_SUCC, MESSAGE_INFO )
        except:
            self._error( traceback.format_exc() )
            db.rollback()
            flash( MSG_SERVER_ERROR, MESSAGE_ERROR )
            return redirect( url_for( '.view' ) )
        else:
            return redirect( url_for( '.view', action = 'view', id = hdr.id ) )


    @templated( 'update.html' )
    @allPermission( ['DN_EDIT', ] )
    def update( self ):
        did = _g( 'id' )
        if not did :
            flash( MSG_RECORD_NOT_EXIST, MESSAGE_ERROR )
            return  redirect( url_for( '.view' ) )

        obj = DN.get( did )
        if not obj :
            flash( MSG_RECORD_NOT_EXIST, MESSAGE_ERROR )
            return  redirect( url_for( '.view' ) )

        #=======================================================================
        # to be update in the future
        #=======================================================================
        # it's get method ,render the update page
        if request.method != 'POST':
            spobj = getCurrentShopProfile()
            shops = qry( Shop ).filter( and_( Shop.active == 0, Shop.id != spobj.shopID ) ).order_by( Shop.name )
            dtldata = []
            for d in obj.dtls:
                try:
                    ( ivt, ivtip ) = qry( InventoryLocation, InventoryProduct ).filter( and_( InventoryProduct.pdtID == d.pdtID,
                                                                  InventoryProduct.ivtID == d.sIvtLtnID,
                                                                  InventoryProduct.ivtID == InventoryLocation.id ) ).one()

                    dtldata.append( [d, ivt, ivtip, ] )
                except:
                    dtldata.append( [d, None, None] )
            return {'shops' : shops, 'obj' : obj, 'dtldata' : dtldata}


        # save the post request
        try:
            oldCopy = obj.serialize()
            extLog = []
            params = _gld( 'dShopID', 'awb', 'expectDate', 'fee', 'remark', 'createTime' )
            obj.update( params )

            for d in obj.dtls:
                if not _g( 'olddtl_qty_%s' % d.id ) :
                    extLog.append( u'删除商品[%s],数量[%s],备注[%s]。' % ( d.pdtname, d.qty, d.remark ) )
                    d.active = 1    # if the qty is 0 or not exist ,remove the dtl
                oldDtl = d.serialize()
                d.qty = _g( 'olddtl_qty_%s' % d.id )
                d.remark = _g( 'olddtl_rmk_%s' % d.id )
                newDtl = d.serialize()
                tmpLog = checkUpdate( oldDtl, newDtl )
                if tmpLog : extLog.extend( [u'修改商品 [%s]：' % d.pdtname, ] + tmpLog )
                d.copyPdtInfo( d.pdt )

            for ( k, v ) in _gp( 'dtl_qty_' ):
                n1, n2, pid, vid = k.split( "_" )
                tid = "%s_%s" % ( pid, vid )
                qty = _g( 'dtl_qty_%s' % tid )
                rmk = _g( 'dtl_rmk_%s' % tid )
                if not qty or int( qty ) < 1 : continue
                dtl = DNDtl.create( dict( hdr = obj, pdtID = pid, qty = qty, remark = rmk, sIvtLtnID = vid ) )
                dtl.copyPdtInfo( Product.get( pid ) )
                db.add( dtl )

            newCopy = obj.serialize()
            makeUpdateLog( obj, oldCopy, newCopy, extLog )
            db.commit()
            flash( MSG_UPDATE_SUCC, MESSAGE_INFO )
        except:
            self._error( traceback.format_exc() )
            db.rollback()
            flash( MSG_SERVER_ERROR, MESSAGE_ERROR )
        return redirect( url_for( '.view', action = 'view', id = obj.id ) )




    def app( self ):
        try:
            tid, status = _g( 'id' ) , _g( 'sts' )
            obj = DN.get( tid )
            obj.status = status

            if status == unicode( TNS_SND_AP ):    # if approved ,then change the inventory qty
                tvlivt = InventoryLocation.getInTravelIvt()

                # create inventory note for IN
                inspobj = obj.dShop.getProfile()
                inNtHdr = InventoryNote.create( dict( shopID = inspobj.shopID, ivtID = tvlivt.id, direction = 'IN', refer = obj.no, ) )
                inNtHdr.comeFrom = ( obj, obj.id )
                # create inventory note for OUT
                outspobj = getCurrentShopProfile()
                outNtHdr = InventoryNote.create( dict( shopID = outspobj.shopID, ivtID = tvlivt.id, direction = 'OUT', refer = obj.no, ) )
                outNtHdr.comeFrom = ( obj, obj.id )

                db.add_all( [inNtHdr, outNtHdr] )
                inDtlGrp = {}

                for d in obj.dtls:
                    outDtl = InventoryNoteDtl.create( dict( hdr = outNtHdr, pdtID = d.pdtID, qty = d.qty, sIvtLtnID = d.sIvtLtnID, dIvtLtnID = tvlivt.id ) )
                    outDtl.copyPdtInfo( d.pdt )
                    db.add( outDtl )

                    #===========================================================
                    # group the IN inventory note's product
                    #===========================================================
                    if d.pdtID not in inDtlGrp:
                        inDtl = InventoryNoteDtl.create( dict( hdr = inNtHdr, pdtID = d.pdtID, qty = d.qty, sIvtLtnID = tvlivt.id, dIvtLtnID = inspobj.inventoryID ) )
                        inDtl.copyPdtInfo( d.pdt )
                        inDtlGrp[d.pdtID] = inDtl
                        db.add( inDtl )
                    else:
                        inDtlGrp[d.pdtID].qty += d.qty

                    # going to move the product from the source to the dest
#                     InventoryProduct.goingToMove( outDtl.sIvtLtnID, outDtl.dIvtLtnID, d.pdtID, d.qty )

#                 for inDtl in inDtlGrp.values():
                    # going to move the product from the source to the dest
#                     InventoryProduct.goingToMove( inDtl.sIvtLtnID, inDtl.dIvtLtnID, inDtl.pdtID, inDtl.qty )

                db.add( SysLog( refClz = obj.__class__.__name__, type = LOG_TYPE_APPROVE, refID = obj.id, remark = None ) )
            else:
                db.add( SysLog( refClz = obj.__class__.__name__, type = LOG_TYPE_REJECT, refID = obj.id, remark = None ) )

            flash( MSG_UPDATE_SUCC, MESSAGE_INFO )
            db.commit()
        except:
            self._error( traceback.format_exc() )
            db.rollback()
            flash( MSG_SERVER_ERROR, MESSAGE_ERROR )
        return redirect( url_for( '.view' ) )


    def delete( self ):
        try:
            tid = _g( 'id' )
            obj = DN.get( tid )
            if obj.status == TNS_SND_AP:
                flash( MSG_NO_SUCH_ACTION, MESSAGE_ERROR )
            else:
                obj.active = INACTIVE
                db.add( SysLog( refClz = obj.__class__.__name__, type = LOG_TYPE_DEL, refID = obj.id, ) )
                flash( MSG_DELETE_SUCC, MESSAGE_INFO )
                db.commit()
        except:
            db.rollback()
            flash( MSG_SERVER_ERROR )
        return redirect( url_for( '.view' ) )


    @templated( "searchpdt.html" )
    def searchpdt( self ):
        form = srhPdtForm( request.form )
        cds = [InventoryProduct.active == ACTIVE, Product.active == ACTIVE, InventoryLocation.active == ACTIVE,
               InventoryProduct.pdtID == Product.id, InventoryProduct.ivtID == InventoryLocation.id,
               InventoryLocation.name != 'PROCESS',
#                InventoryLocation.name != 'WASTAGE',
               InventoryLocation.name != 'PURCHASE', InventoryLocation.name != 'SOLD_OUT',
               InventoryProduct.qty > 0,
               ]

        if _g( 'no' ) : cds.append( Product.no.like( '%%%s%%' % _g( 'no' ) ) )
        if _g( 'name' ) : cds.append( Product.name.like( '%%%s%%' % _g( 'name' ) ) )
        if _g( 'styleNo' ) : cds.append( Product.styleNo.like( '%%%s%%' % _g( 'name' ) ) )
        if _g( 'certificate' ) : cds.append( Product.certificate.like( '%%%s%%' % _g( 'name' ) ) )

        result = qry( Product, InventoryLocation, InventoryProduct ).filter( and_( *cds ) ).order_by( Product.name )
        return {'form' : form, 'result' : result}


    @templated( 'applysend.html' )
    def applySend( self ):
        items = _gl( 'item' )
        if not items :
            flash( MSG_NOT_ENOUGH_PARAMS, MESSAGE_ERROR )
            return redirect( url_for( '.view', action = 'searchpdt' ) )

        if type( items ) != list : items = [items, ]
        data = {}
        spobj = getCurrentShopProfile()
        for item in items:
            n, pdtID, ivtID = item.split( "_" )
            cds = [Product.active == 0, InventoryLocation.active == 0, InventoryProduct.active == 0,
                   Shop.active == 0,
                   InventoryProduct.pdtID == Product.id, InventoryProduct.ivtID == InventoryLocation.id,
                   Shop.id == InventoryLocation.referID, Product.id == pdtID, InventoryLocation.id == ivtID,
                   ]

            pdtobj, sobj, ivtobj, ipobj = qry( Product, Shop, InventoryLocation, InventoryProduct ).filter( and_( *cds ) ).one()
            if ivtobj.referID == spobj.shopID: continue    # filter out the current shop's item
            if sobj.id not in data :
                data[sobj.id] = {'shop' : sobj, 'data' : [( pdtobj, ivtobj, ipobj ), ]}
            else: data[sobj.id]['data'].append( ( pdtobj, ivtobj, ipobj ) )
        return {'data' : data}


    def ajaxAddDN( self ):
        try:
            params = {}
            params['sShopID'] = _g( 'shopID' )
            params['dShopID'] = getCurrentShopID()
            hdr = DN.create( params )
            db.add( hdr )

            for k, v in _gp( "qty_" ) :
                n, pdtID, ivtID = k.split( "_" )
                dtl = DNDtl.create( {'hdr' : hdr, 'pdtID' : pdtID, 'sIvtLtnID' : ivtID, 'qty' : v, } )    # sIvtLtnID
                dtl.copyPdtInfo( Product.get( pdtID ) )
                db.add( dtl )
            db.commit()
            return jsonify( {'code' : 0 , 'msg' : MSG_SAVE_SUCC} )
        except:
            db.rollback()
            self._error( traceback.format_exc() )
            return jsonify( {'code' : 1 , 'msg' : MSG_SERVER_ERROR} )



    @templated( 'view_log.html' )
    def viewLog( self ):
        id = _g( 'id' )
        if not id : abort( 404 )
        obj = DN.get( id )
        if not obj : abort( 404 )
        return {'obj' : obj}



    def ajaxSrhPdt( self ):
        f = _g( 'f' )
        q = _g( 'q' )

        if not f : return jsonify( {'code' : 1, 'msg' : MSG_NOT_ENOUGH_PARAMS, 'data' : []} )
        try:
            spobj = getCurrentShopProfile()
            cs = [
                  Product.active == 0, InventoryProduct.active == 0, InventoryLocation.active == 0,
                  Product.id == InventoryProduct.pdtID, InventoryLocation.id == InventoryProduct.ivtID,
#                   InventoryProduct.qty > 0 ,
                  InventoryProduct.availableQty > 0,    # the available qty should be large than 0 ,then could be transfered
                  InventoryLocation.referID == spobj.shopID,
                  ]
            if q : cs.append( getattr( Product, f ).like( '%%%s%%' % q ) )
            result = qry( Product, InventoryLocation, InventoryProduct ).filter( and_( *cs ) ).order_by( Product.no )

            data = {}
            for ( pdt, ivt, ip ) in result :
                if pdt.id not in data : data[pdt.id] = {
                                                        'pid' : pdt.id, 'no' : pdt.no, 'name' : pdt.name,
                                                        'desc' : pdt.makeDesc(),
                                                        'ivts' : [( ivt.id, unicode( ivt.fullPath ), ip.availableQty ), ] }
                else: data[pdt.id]['ivts'].append( ( ivt.id, unicode( ivt.fullPath ), ip.availableQty ) )
            return jsonify( {
                            'code' : 0 , 'msg' : 'OK',
                            'data' : data.values(),
                            } )
        except:
            self._error( traceback.format_exc() )
            return jsonify( {'code' : 1, 'msg' : MSG_SERVER_ERROR} )




bpTns.add_url_rule( '/', view_func = TransferView.as_view( 'view' ), defaults = {'action':'index'} )
bpTns.add_url_rule( '/<action>', view_func = TransferView.as_view( 'view' ) )


from wtforms import Form, TextField, validators
from sys2do.util.wt_helper import MyDateField

class srhForm( Form ):
    no = TextField( u'系统编号', [validators.Length( min = 1, max = 25 )] )
    awb = TextField( u'送货单号', [validators.Length( min = 1, max = 25 )] )
    createTimeFrom = MyDateField( u'开单时间(开始)' )
    createTimeTo = MyDateField( u'开单时间(结束)' )


class srhPdtForm( Form ):
    no = TextField( u'商品编号' )
    name = TextField( u'商品名称' )
    styleNo = TextField( u'款号' )
    certificate = TextField( u'证书号' )
